package com.tsystems.train.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import com.tsystems.train.dao.StationDAO;
import com.tsystems.train.dao.TimeTableElementDAO;
import com.tsystems.train.dao.TrainDAO;
import com.tsystems.train.entity.Station;
import com.tsystems.train.entity.TimeTableElement;
import com.tsystems.train.entity.Train;

/**
 * 
 * @author Turybrin Vladislav
 * 
 *         The class contains methods for performing basic operations with
 *         trains.
 */
@Stateless
public class TimeTableElementServices {

	@EJB
	private TrainDAO trainDAO;

	@EJB
	private StationDAO stationDAO;

	@EJB
	private TimeTableElementDAO timeTableElementDAO;

	/**
	 * Add new element of timetable if it has not already added
	 * 
	 * @param timeTableElement
	 *            
	 * @return "Element of timetable added" if it so,
	 *         otherwise -  message with information about error.
	 */
	public String addTimeTableElement(TimeTableElement timeTableElement) {

		if (timeTableElement.getTrain() == null) {
			return "Train required";
		}

		if (timeTableElement.getArrivalStation() == null) {
			return "Arrival station required";
		}

		if (timeTableElement.getDepartureStation() == null) {
			return "Departure station required";
		}
		Train trainFound = trainDAO.getTrainByNumber(timeTableElement
				.getTrain().getNumber());
		if (trainFound == null) {
			return "Train not found ";
		}

		Station arrivalFound = stationDAO.getStationByName(timeTableElement
				.getArrivalStation().getName());

		if (arrivalFound == null) {
			return "Arrival station not found ";
		}

		Station departureFound = stationDAO.getStationByName(timeTableElement
				.getDepartureStation().getName());

		if (departureFound == null) {
			return "Departure station not found";
		}
		if (timeTableElement.getArrivalTime().compareTo(
				timeTableElement.getDepartureTime()) < 0) {
			return "Departure time later than arrival time";
		}

		timeTableElement = new TimeTableElement(trainFound, departureFound,
				arrivalFound, timeTableElement.getDepartureTime(),
				timeTableElement.getArrivalTime());

		TimeTableElement timeTableElementFound = timeTableElementDAO
				.getEqualsTimeTableElement(timeTableElement);

		if (timeTableElementFound != null) {
			return "This element of time table already exist";
		}

		timeTableElementDAO.insert(timeTableElement);
		return "Element of timetable added";
	}

	/**
	 * Get all elements of timetable from database.
	 * 
	 * @return list of elements.
	 */
	public List<TimeTableElement> viewTimeTableElementsBase() {

		List<TimeTableElement> timeTableElements = timeTableElementDAO
				.getTimeTableElementBase();
		return timeTableElements;

	}

	/**
	 * Find all trains from station A to station B in a range of time
	 * 
	 * @param stationA
	 * 
	 * @param stationB
	 * 
	 * @param timeA
	 *            - start of range
	 * @param timeB
	 *            -end of range
	 * @return HashMap <Train, path represented as list of timetable elements.
	 * 
	 */
	public HashMap<Train, List<TimeTableElement>> findTrain(Station stationA,
			Station stationB, Date timeA, Date timeB) {

		stationA = stationDAO.getStationByName(stationA.getName());
		stationB = stationDAO.getStationByName(stationB.getName());

		List<TimeTableElement> departureList = timeTableElementDAO
				.getTimeTableElementsByDepartureStation(stationA);
		List<TimeTableElement> arrivalList = timeTableElementDAO
				.getTimeTableElementsByArrivalStation(stationB);

		Iterator<TimeTableElement> departureIter = departureList.iterator();
		while (departureIter.hasNext()) {
			TimeTableElement departureTTE = departureIter.next();
			if (departureTTE.getDepartureTime().compareTo(timeA) < 0) {
				departureIter.remove();
			}
		}

		Iterator<TimeTableElement> arrivalIter = arrivalList.iterator();
		while (arrivalIter.hasNext()) {
			TimeTableElement arrivalTTE = arrivalIter.next();
			if (arrivalTTE.getArrivalTime().compareTo(timeB) > 0) {
				arrivalIter.remove();
			}
		}
		if (departureList.isEmpty() || arrivalList.isEmpty()) {
			return new HashMap<Train, List<TimeTableElement>>();
		}
		HashMap<Train, List<TimeTableElement>> resultList = new HashMap<Train, List<TimeTableElement>>();

		for (TimeTableElement arrivalElement : arrivalList) {
			for (TimeTableElement departureElement : departureList) {
				if (arrivalElement.getTrain().equals(
						departureElement.getTrain())) {
					List<TimeTableElement> tteList = mergePath(
							departureElement, arrivalElement);
					if (tteList.isEmpty()) {
						continue;
					}
					resultList.put(arrivalElement.getTrain(), tteList);
				}
			}
		}

		return resultList;
	}

	/**
	 * Trying to build path between 2 elements of timetable by merging timetable
	 * elements. For successful merging, arrival information from one element
	 * and departure information from another must be equal.
	 * 
	 * 
	 * @param startElement
	 * 
	 * @param finishElement
	 *            -
	 * @return empty list, if path is incorrect, otherwise path represented as
	 *         list of timetable elements
	 */
	public List<TimeTableElement> mergePath(TimeTableElement startElement,
			TimeTableElement finishElement) {

		if (!startElement.getTrain().equals(finishElement.getTrain())) {
			return Collections.emptyList();
		}
		ArrayList<TimeTableElement> result = new ArrayList<TimeTableElement>(20);
		result.add(startElement);
		if (startElement.equals(finishElement)) {
			return result;
		}

		if (timeTableElementDAO.getEqualsTimeTableElement(startElement) == null) {
			return Collections.emptyList();
		}
		if (timeTableElementDAO.getEqualsTimeTableElement(finishElement) == null) {
			return Collections.emptyList();
		}
		List<TimeTableElement> pathParts = timeTableElementDAO
				.getTimeTableElementsByTrain(startElement.getTrain());
		TimeTableElement currentElement = startElement;
		Iterator<TimeTableElement> iterTTE = pathParts.iterator();
		TimeTableElement tempElem;
		while (!pathParts.isEmpty()) {
			iterTTE = pathParts.iterator();
			tempElem = null;
			while (iterTTE.hasNext()) {
				TimeTableElement element = iterTTE.next();
				if (startElement.equals(element)) {
					iterTTE.remove();
					continue;
				}
				if (currentElement.getArrivalStation().equals(
						element.getDepartureStation())
						&& currentElement.getArrivalTime().equals(
								element.getDepartureTime())) {
					if (tempElem != null) {

						return Collections.emptyList();
					}
					tempElem = element;
					result.add(tempElem);
					iterTTE.remove();

				}
			}
			if (finishElement.equals(tempElem)) {
				return result;
			}
			if (tempElem != null) {
				currentElement = tempElem;
			} else {
				return Collections.emptyList();
			}

		}

		return Collections.emptyList();

	}

	/**
	 * Get all timetable elements from database by departure station.
	 * 
	 * @param station
	 *            - departure station.
	 * @return timetable elements.
	 */
	public List<TimeTableElement> getTimeTable(Station station) {

		List<TimeTableElement> departureList = timeTableElementDAO
				.getTimeTableElementsByDepartureStation(station);

		return departureList;
	}

	/**
	 * Get all timetable elements from database by arrival station.
	 * 
	 * @param station
	 *            - arrival station.
	 * @return timetable elements.
	 */
	public List<TimeTableElement> getArrivalTimeTable(Station station) {

		List<TimeTableElement> arrivalList = timeTableElementDAO
				.getTimeTableElementsByArrivalStation(station);

		return arrivalList;
	}

}
